iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
Software Development

從零打造邊緣運算閘道器:Raspberry Pi 與 Linux 底層軟硬整合實戰系列 第 15

Day 15 - losetup 的藝術:如何優雅地掛載與回收 Loop Device?

  • 分享至 

  • xImage
  •  

[!NOTE]
本系列文章已進行全域去敏與匿名化處理,專案代號統一為 DataGateway,內網 IP 統一為 10.0.0.x

昨天我們用 dd 創造了一個 .img 檔案,並且將它格式化為 FAT32,最後透過 ConfigFS 餵給了 Windows。Windows 現在開心得不得了,它看見了一個完整的磁碟區。

但是,身為這個 .img 檔案主人的 Raspberry Pi (Linux) 本身,卻不知道該怎麼把資料寫進這個 FAT32 空間!對 Linux 來說,它只是一個存在 /opt/gateway/usb_disk.img 裡面的龐大檔案而已,你沒辦法直接用 open() 寫入資料到這個「檔案的檔案系統」中。

這時候,我們需要一座橋樑,把這個實體檔案偽裝成一個區塊設備 (Block Device)
這就是 Loop Device (迴圈設備) 發揮作用的時刻。

問題情境:如何掛載一個檔案?

平時我們掛載隨身碟,通常是針對硬體節點,例如:
mount /dev/sdb1 /mnt/usb

但現在我們手上沒有 /dev/sdb1,我們只有一個 usb_disk.img
如果你直接執行 mount /opt/gateway/usb_disk.img /mnt/usb,在較新版本的 Linux 系統中,mount 其實非常聰明,它會在背景幫你搞定一切。但這背後的魔法,其實就是調用了 losetup

在醫療邊緣設備的開發中,把邏輯交給 mount 的隱式行為是危險的。我們需要對系統資源有絕對的控制權,這包含明確知道我們佔用了哪個 Loop Device,以及在拔除隨身碟時能乾淨地回收資源。

什麼是 Loop Device?

Loop Device 是一個虛擬的 Block Device,通常位於 /dev/loop0, /dev/loop1 等位置。
它的作用很簡單:把讀寫這些 /dev/loopX 的操作,透明地轉發到某個實體的檔案上。

這就像是用一根水管 (Loop Device) 插入那個水缸 (img 檔案),然後你在水管的另一頭安裝一個水龍頭 (mount point),你轉開水龍頭流出來的水,其實就是水缸裡面的水。

實戰:優雅地掛載與回收

在我們的 DataGateway 中,Python 程式啟動時,必須自己去掛載這個虛擬空間,才能開始把 Sensor 送來的資料寫入。

1. 尋找空閒的 Loop Device

Linux 系統內建了多個 Loop Device,我們不該死寫 /dev/loop0,因為它可能已經被 Docker 或其他系統服務佔用了(例如 Ubuntu 的 snap 系統就吃了超多 loop device)。

我們應該讓系統自己分配:

# 尋找第一個未被使用的 loop device
LOOP_DEV=$(losetup -f)
echo "找到空閒設備:$LOOP_DEV"
# 可能會輸出 /dev/loop0

2. 綁定檔案到 Loop Device

找到空閒的水管後,把它插進水缸裡:

sudo losetup $LOOP_DEV /opt/gateway/usb_disk.img

這時,/dev/loop0 就等同於那顆 FAT32 硬碟了。

3. 掛載檔案系統

有了 Block Device 後,我們就能用標準的 mount 指令把它掛載到指定的資料夾:

# 建立掛載點
sudo mkdir -p /mnt/usb_sync

# 將 FAT32 系統掛載起來,指定 uid 與 gid 為 pi,避免權限問題
sudo mount -t vfat -o uid=1000,gid=1000,umask=022 $LOOP_DEV /mnt/usb_sync

現在,Python 程式只要對 /mnt/usb_sync 寫入 data.csv,實際上就是在改變 usb_disk.img 裡面的二進位結構!

4. 優雅地回收 (Clean-up)

當不需要使用時,如果不正確地卸載,很容易導致檔案系統毀損。卸載必須是掛載的反向操作:

# 1. 卸載掛載點
sudo umount /mnt/usb_sync

# 2. 拔除水管 (釋放 Loop Device)
sudo losetup -d $LOOP_DEV

你可以用 losetup -a 來查看當前系統有哪些檔案被綁定在哪些 Loop Device 上,這在 Debug 時非常有用。

Python 中的實作 (去敏版)

為了自動化,我們在 DataGateway 中包裝了一支 Python 腳本來處理這件事:

import subprocess
import os

def mount_virtual_disk(img_path, mount_point):
    if not os.path.exists(mount_point):
        os.makedirs(mount_point)
        
    # 尋找空閒 loop device
    loop_dev = subprocess.check_output(['losetup', '-f']).decode().strip()
    
    # 綁定
    subprocess.run(['sudo', 'losetup', loop_dev, img_path], check=True)
    
    # 掛載
    subprocess.run(['sudo', 'mount', '-t', 'vfat', 
                    '-o', 'uid=1000,gid=1000', 
                    loop_dev, mount_point], check=True)
    
    return loop_dev

def unmount_virtual_disk(mount_point, loop_dev):
    # 確保所有緩存寫入硬碟
    subprocess.run(['sync'])
    subprocess.run(['sudo', 'umount', mount_point], check=True)
    subprocess.run(['sudo', 'losetup', '-d', loop_dev], check=True)

危機降臨前夕

好啦,現在我們有兩端可以存取這個 usb_disk.img 了:

  1. 外部的 Windows PC (透過 USB OTG 直接存取)
  2. 內部的 Raspberry Pi (透過 Loop Device 與 mount 存取)

聽起來很完美對吧?設備產生資料 ➔ 透過 Loop Device 寫入 ➔ Windows 透過 USB 即時看見變化。

大錯特錯。
這將會引發非常恐怖的檔案系統災難。明天,我們將直面邊緣儲存領域中最棘手的難題:「雙腦分裂 (Split-Brain) 危機」。


上一篇
Day 14 - 虛擬磁碟的誕生:如何用 dd 與 mkdosfs 憑空捏出一個 FAT32 空間?
下一篇
Day 16 - 雙腦分裂 (Split-Brain) 危機:當 PC 與 Pi 同時寫入磁碟的毀滅性後果
系列文
從零打造邊緣運算閘道器:Raspberry Pi 與 Linux 底層軟硬整合實戰26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言